\bibliographystyle{gbt7714-2005}
\chapter{PHP与面向对象}
\label{OOP}

PHP有时会被误认为是一种属于计算机业务爱好者的语言，最适合用于表现层，这是一种有缘由的偏见。因为PHP在一开始，并不支持面向对象编程，直到PHP4才开始大部分面向对象特性。

需要说明的是，虽然面向对象编程在20世纪60年代就已经出现，但是从20世纪90年代中期才开始发展。Java是1995年才发布。C++也是在20世纪90年代才有了真正的飞跃。

PHP5正式发布于 2004 年 7 月。该版本明确表示支持对象和面向对象的程序设计，但这并不是PHP的唯一工作方式。\cite{PHPGroup，2014}PHP对于对象的支持程度，已经足以实现所有面向对象编程的目标。\cite{赞特斯彻，2011}

对象和类是面向对象编程的核心，也是PHP5的核心内容。在下面的部分，我们将介绍对象基础。

\section{类和对象}
类是用于生成对象的代码模版。使用class关键词和一个任意的类名来声明类，使用new操作符来生成对象。
\lstinputlisting[language=PHP,emph={new,class},emphstyle=\bfseries]{example/PHP/05-oop1/01.php}
输出结果：object(ShopProduct)[1]，object(ShopProduct)[2]，通过传递对象给var\_dump()，我们获得了它所包含的有用信息——每个对象的内部标识符。

\section{设置类中的属性}
类可以定义为属性的特定变量。属性也被称为成员变量，类的属性在声明和赋值前需要加一个代表可见性的关键词（public、protected、private），这些关键词决定了属性的作用域。属性的调用使用“->”。
\lstinputlisting[emph={public,protected,private},emphstyle=\bfseries]{example/PHP/05-oop1/02.php}

\section{使用方法}
属性可以让对象存储数据，类方法则可以让对象执行任务。方法是类中声明的特殊函数，也可以接受限定词，包括可见性关键字。和属性一样，方法可以被声明为public、protected或private，默认为public。
\lstinputlisting{example/PHP/05-oop1/03.php}
这时初始化对象依然很麻烦，另外还无法保证所有属性在初始化的时候被设置。因此，我们需要一个当类实例化的时候可被自动调用的类方法。

创建对象时，构造方法\_\_construct()会被自动调用，构造方法用来完成需要准备的工作。
\lstinputlisting[emph={\_\_construct},emphstyle=\bfseries]{example/PHP/05-oop1/04.php}
你所设计的类应该让对象的使用者确定它们的特性。

\section{参数和类型}
PHP是一种弱类型语言，即变量不需要声明为特定的数据类型，这既是一种便利，也是一种麻烦（尤其是当你希望一个参数必须是某种类型的时候）。

为了解决这个问题，PHP5引入了类型提示。要增加一个方法参数的类型提示，只需简单地将类名放在需要约束的方法参数之前。
\lstinputlisting[emph={ShopProduct},emphstyle=\bfseries]{example/PHP/06-oop2/03.php}
类型提示不能用于规定参数为某种基本数据类型（如字符串、整型等等）。

\section{继承}

继承是从一个基类得到一个或多个派生类的机制。创建继承树的第一步是找到现有基类元素中不适合放在一起，或者不需要进行特殊处理的类方法。要创建一个子类，必须在类声明中使用extends关键字。子类默认继承父类所有的public和protected方法，但是不能继承private方法或属性。子类可以扩展和修改父类的功能。

在子类中定义构造方法时，如果父类构造方法是有参数的，那么需要传递参数给父类的构造方法，否则你得到的可能是一个构造不完整的对象。
\lstinputlisting[emph={extends,parent},emphstyle=\bfseries]{example/PHP/05-oop1/05.php}

范围解析操作符（::）或者更简单地说是一对冒号，可以用于访问静态成员，类常量，还可以用于覆盖类中的属性和方法。如上例中的：

\begin{lstlisting}
parent::__construct()
\end{lstlisting}

\section{抽象类}
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类，如果它里面至少有一个方法是被声明为抽象的，那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式（参数），不能定义其具体的功能实现。继承一个抽象类的时候，子类必须定义父类中的所有抽象方法。另外，这些方法的访问控制必须和父类中一样（或者更为宽松）。例如某个抽象方法被声明为受保护的，那么子类中实现的方法就应该声明为受保护的或者公有的，而不能定义为私有的。此外方法的调用方式必须匹配，即类型和所需参数数量必须一致。

\begin{lstlisting}
<?php
abstract class AbstractClass
{
 // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法（非抽象方法）
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>
\end{lstlisting}

以上代码输出结果为：

\begin{lstlisting}
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
\end{lstlisting}

\section{接口}
使用接口（interface），可以指定某个类必须实现哪些方法，但不需要定义这些方法的具体内容。

接口是通过 interface 关键字来定义的，就像定义一个标准的类一样，但其中定义所有的方法都是空的。接口也可以继承。

接口中定义的所有方法都必须是公有，这是接口的特性。

要实现一个接口，使用 implements 操作符。类中必须实现接口中定义的所有方法，否则会报一个致命错误。类可以实现多个接口，用逗号来分隔多个接口的名称。

\begin{lstlisting}
<?php
interface a
{
    public function foo();
}

interface b
{
    public function bar();
}

interface c extends a, b
{
    public function baz();
}

class d implements c
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}
?>
\end{lstlisting}


\section{自动加载类}



\bibliography{../bib/yangjh}
